#include "logcplus_buffer.h"

namespace logcplus
{
	logcplus_buffer::logcplus_buffer()
	{
		m_buffer = nullptr;
		m_cur_buffer_capacity = sizeof(m_stack_buffer);
		m_cur_buffer_data_len = 0;
		memset(m_stack_buffer,0x00,sizeof(m_stack_buffer));
	}

	logcplus_buffer::logcplus_buffer(const logcplus_buffer& ilogcplus_buffer)
	{
		if(this != &ilogcplus_buffer)
		{
			m_buffer = ilogcplus_buffer.m_buffer;
			m_cur_buffer_capacity = ilogcplus_buffer.m_cur_buffer_capacity;
			m_cur_buffer_data_len = ilogcplus_buffer.m_cur_buffer_data_len;
			memcpy(m_stack_buffer,ilogcplus_buffer.m_stack_buffer,sizeof(m_stack_buffer));
		}
	}

	logcplus_buffer& logcplus_buffer::operator=(const logcplus_buffer& ilogcplus_buffer)
	{
		if(this != &ilogcplus_buffer)
		{
			m_buffer = ilogcplus_buffer.m_buffer;
			m_cur_buffer_capacity = ilogcplus_buffer.m_cur_buffer_capacity;
			m_cur_buffer_data_len = ilogcplus_buffer.m_cur_buffer_data_len;
			memcpy(m_stack_buffer,ilogcplus_buffer.m_stack_buffer,sizeof(m_stack_buffer));
		}
		return *this;
	}

	logcplus_buffer::logcplus_buffer(uint64_t nBufferCapacity)
		: m_cur_buffer_capacity(nBufferCapacity)
	{
		m_buffer = nullptr;
		m_cur_buffer_data_len = 0;
		memset(m_stack_buffer,0x00,sizeof(m_stack_buffer));
	}

	logcplus_buffer::~logcplus_buffer()
	{
		free_log_buffer();
	}

	int logcplus_buffer::init_log_buffer()
	{
		std::lock_guard<std::mutex> _lock(m_lock);

		m_cur_buffer_capacity = m_cur_buffer_capacity > 4*1024*1024 ? sizeof(m_stack_buffer) : m_cur_buffer_capacity;
		if(m_cur_buffer_capacity > sizeof(m_stack_buffer))
		{
			if(m_buffer == nullptr)
			{
				m_buffer = (char*)calloc(m_cur_buffer_capacity,sizeof(char));
				if(m_buffer == nullptr)
				{
					printf("PID[%d] malloc size [%lu] failure,errno:%d\n",getpid(),m_cur_buffer_capacity,errno);
					return -1;
				}
				memcpy(m_buffer,m_stack_buffer,m_cur_buffer_data_len);
				memset(m_stack_buffer,0x00,sizeof(m_stack_buffer));	
			}
		}
		else if(m_cur_buffer_capacity < 1024)
		{
			m_cur_buffer_capacity = maxStackBufferSize;
		}
		return 0;
	}

	int logcplus_buffer::free_log_buffer()
	{
		std::lock_guard<std::mutex> _lock(m_lock);
		if(m_buffer != nullptr)
		{
			free(m_buffer);
			m_buffer = nullptr;
		}
		m_cur_buffer_data_len = 0;
		memset(m_stack_buffer,0x00,sizeof(m_stack_buffer));
		return 0;
	}

	int logcplus_buffer::write_buffer(const char* data,FILE* fp)
	{
		if(data == nullptr) return 0;
		uint64_t buffer_len=strlen(data);


		//Check the remaining buffer size if less than log context length and
		//The current buffer length is less than the default stack size
		if(m_cur_buffer_capacity <= maxStackBufferSize)
		{
			if(m_cur_buffer_capacity-m_cur_buffer_data_len < buffer_len)
			{
				std::lock_guard<std::mutex> _lock(m_lock);
				if(fp)
				{
					if(strlen(m_stack_buffer) > 0)
					{
						//fprintf(fp,"%s",m_stack_buffer);
						fwrite(m_stack_buffer,strlen(m_stack_buffer),1,fp);
					}
					if(buffer_len > m_cur_buffer_capacity-1)
					{
						//fprintf(fp,"%s",data);
						fwrite(data,buffer_len,1,fp);
						buffer_len = 0;
					}	
					fflush(fp);
					free_log_buffer();
				}
				else
				{
					buffer_len = 0;
				}
			}
		}

		std::lock_guard<std::mutex> _lock(m_lock);

		if(m_cur_buffer_capacity > maxStackBufferSize)
		{
			init_log_buffer();
			if(m_cur_buffer_capacity-m_cur_buffer_data_len < buffer_len)
			{
				if(fp)
				{
					int x_buffer_len = strlen(m_buffer);
					if(x_buffer_len > 0)
					{
						//fprintf(fp,"%s",m_buffer);
						fwrite(m_buffer,x_buffer_len,1,fp);
					}
					if(buffer_len > m_cur_buffer_capacity - 1)
					{
						//fprintf(fp,"%s",data);
						fwrite(data,buffer_len,1,fp);
						buffer_len = 0;
					}	
					fflush(fp);
					free_log_buffer();
				}
				else
				{
					buffer_len = 0;
				}	
			}
		}
		if(buffer_len == 0)
		{
			return 0;
		}
		if(m_cur_buffer_capacity > maxStackBufferSize)
		{
			memcpy(m_buffer+m_cur_buffer_data_len,data,buffer_len);
			m_buffer[m_cur_buffer_data_len+buffer_len]=0x00;		
		}
		else
		{
			memcpy(m_stack_buffer+m_cur_buffer_data_len,data,buffer_len);
			m_stack_buffer[m_cur_buffer_data_len+buffer_len]=0x00;
		}
		m_cur_buffer_data_len += buffer_len;
		return 0;
	}
}
